package com.weifly.weistock.optionvix.impl;

import com.weifly.weistock.core.util.WeistockUtils;
import com.weifly.weistock.core.util.visitor.PreviousVisitor;
import com.weifly.weistock.optionvix.OptionVixConstant;
import com.weifly.weistock.optionvix.OptionVixConverter;
import com.weifly.weistock.optionvix.bo.VixDayBO;
import com.weifly.weistock.optionvix.bo.VixDayExternalBO;
import com.weifly.weistock.optionvix.bo.VixYearBO;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

/**
 * VIX附加信息计算器
 *
 * @author weifly
 * @since 2021/1/27
 */
public class VixDayExternalCalculator {

    private Collection<VixYearBO> yearList; // 原始数据集合
    private TreeMap<String, VixDayExternalBO> dayExternalMap = new TreeMap<>(); // 附加信息Map
    private VixDayBO currentDay = null; // 当天
    private VixDayBO nextDay = null; // 下一天
    private LinkedList<VixDayBO> recentDayList = new LinkedList<>(); // 用于计算recent百分位
    private ArrayList<VixDayBO> allDayList = new ArrayList<>(); // 用于计算all百分位

    public VixDayExternalCalculator(Collection<VixYearBO> yearList) {
        this.yearList = yearList;
    }

    public Map<String, VixDayExternalBO> calc() {
        PreviousVisitor<VixDayBO> iter = new PreviousVisitor(this.yearList);
        while (iter.hasNext()) {
            VixDayBO vixDay = iter.next();
            this.createVixDayExternal(vixDay);
            // 计算diff、rate
            this.calcDiffRate(vixDay);
            // recent百分位
            this.recentDayList.addFirst(vixDay);
            if (this.recentDayList.size() >= OptionVixConstant.MAX_RECENT_DAY_NUMBER) {
                this.removeAndCalcRecentPercent();
            }
            // all百分位
            this.allDayList.add(vixDay);
        }
        // 计算剩余的recent百分位
        while (this.recentDayList.size() >= 10) {
            this.removeAndCalcRecentPercent();
        }
        // 计算all百分位
        this.calcAllPercent();

        return dayExternalMap;
    }

    private void createVixDayExternal(VixDayBO vixDay) {
        VixDayExternalBO vixDayExternal = new VixDayExternalBO();
        vixDayExternal.setDay(vixDay.getDay());
        dayExternalMap.put(vixDay.getDay(), vixDayExternal);
    }

    private void calcDiffRate(VixDayBO vixDay) {
        this.nextDay = this.currentDay;
        this.currentDay = vixDay;
        if (this.nextDay == null || this.currentDay == null) {
            return;
        }

        VixDayExternalBO nextExternal = this.dayExternalMap.get(this.nextDay.getDay());
        double diff = WeistockUtils.subtract(this.nextDay.getCloseValue(), this.currentDay.getCloseValue());
        double rate = new BigDecimal(WeistockUtils.multi(diff, 100)).divide(new BigDecimal(this.currentDay.getCloseValue()), 2, RoundingMode.HALF_UP).doubleValue();
        nextExternal.setDiff(diff);
        nextExternal.setRate(rate);
    }

    private void removeAndCalcRecentPercent() {
        int smallDayNumber = 0;
        int recentDayNumber = this.recentDayList.size();
        VixDayBO targetDay = this.recentDayList.removeLast();
        for (VixDayBO vixDay : this.recentDayList) {
            if (targetDay.getCloseValue() > vixDay.getCloseValue()) {
                smallDayNumber++;
            }
        }
        double percent = OptionVixConverter.computePercent(smallDayNumber + 1, recentDayNumber);
        this.dayExternalMap.get(targetDay.getDay()).setRecentPercent(percent);
    }

    private void calcAllPercent() {
        int allDayNumber = this.allDayList.size();
        for (VixDayBO targetDay : this.allDayList) {
            int smallDayNumber = 0;
            for (VixDayBO vixDay : this.allDayList) {
                if (targetDay.getCloseValue() > vixDay.getCloseValue()) {
                    smallDayNumber++;
                }
            }
            double percent = OptionVixConverter.computePercent(smallDayNumber + 1, allDayNumber);
            this.dayExternalMap.get(targetDay.getDay()).setAllPercent(percent);
        }
    }
}
